package com.hc.bean;

import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;

import java.beans.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ：hc
 * @date ：Created in 2021/7/11 17:57
 * @modified By：
 * JavaBean相关操作封装
 */
public class BeanUtil {

    /**
     * 缓存类、字段和get方法对应信息
     */
    private static final Map<Class<?>, Map<String, Method>> CACHE_MAP = Maps.newHashMapWithExpectedSize(3);


    /**
     * 使用内省来获取Bean信息，效率最高
     * 循环解析1000w次，7s
     * @param o 实体类
     * @return Map<String, Object>
     */
    public static Map<String, Object> classToMapIntrospector(Object o) {
        HashMap<String, Object> map = Maps.newHashMapWithExpectedSize(10);
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(o.getClass());
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }
        assert beanInfo != null;
        // 这个descriptors存储着实体类属性信息、get、set方法
        PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : descriptors) {
            try {
                // 这里得排除class属性
                if ("class".equals(property.getName())) {
                    continue;
                }
                map.put(property.getName(), property.getReadMethod().invoke(o));
            } catch (InvocationTargetException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return map;
    }

    /**
     *
     * 双循环优化，使用map缓存
     * 循环解析1000000(100w)次耗时1s
     * 解析1000w次耗时12s
     * @param o 实体类
     * @return Map<String, Object>
     */
    public static Map<String, Object> classToMap(Object o) {
        HashMap<String, Object> map = Maps.newHashMapWithExpectedSize(10);
        Class<?> clazz = o.getClass();
        // 所有字段已经拿到！
        Field[] fields = clazz.getDeclaredFields();
        // 所有方法拿到
        Method[] methods = clazz.getDeclaredMethods();
        // 缓存没打到，进行字段方法匹配
        if (!CACHE_MAP.containsKey(clazz)) {
            Map<String, Method> fieldMethodMap = Maps.newHashMapWithExpectedSize(10);
            // 字段方法匹配
            for (Field field : fields) {
                for (Method method : methods) {
                    String methodName = method.getName();
                    String fieldName = field.getName();
                    if (methodName.contains("get") && StringUtils.upperCase(methodName).contains(StringUtils.upperCase(fieldName))) {
                        // 匹配好了放缓存里下次使用
                        fieldMethodMap.put(fieldName, method);
                        try {
                            map.put(field.getName(), method.invoke(o));
                        } catch (InvocationTargetException | IllegalAccessException e) {
                            // InvocationTargetException 被调用的方法内部可能有异常抛出没有被捕获，就报这个
                            // IllegalAccessException 没有对这个类的访问权限的时候报这个异常
                            e.printStackTrace();
                        }
                    }
                }
            }
            // 匹配结束了放入缓存
            CACHE_MAP.put(clazz, fieldMethodMap);
        } else {
            // 缓存打到了，直接拿出类信息执行方法赋值
            Map<String, Method> fieldMethod = CACHE_MAP.get(clazz);
            for (String field : fieldMethod.keySet()) {
                try {
                    map.put(field, fieldMethod.get(field).invoke(o));
                } catch (InvocationTargetException | IllegalAccessException e) {
                    // InvocationTargetException 被调用的方法内部可能有异常抛出没有被捕获，就报这个
                    // IllegalAccessException 没有对这个类的访问权限的时候报这个异常
                    e.printStackTrace();
                }
            }
        }
        return map;
    }


    /**
     * 双循环版本 循环解析1000000(100w)次耗时20s
     * @param o 实体类
     * @return Map<String, Object>
     */
    public static Map<String, Object> classToMap20s(Object o) {
        HashMap<String, Object> map = Maps.newHashMapWithExpectedSize(10);
        Class<?> clazz = o.getClass();
        // 所有字段已经拿到！
        Field[] fields = clazz.getDeclaredFields();
        // 所有方法拿到
        Method[] methods = clazz.getDeclaredMethods();
        // 字段方法匹配
        for (Field field : fields) {
            for (Method method : methods) {
                String methodName = method.getName();
                String fieldName = field.getName();
                if (methodName.contains("get") && StringUtils.upperCase(methodName).contains(StringUtils.upperCase(fieldName))) {
                    try {
                        map.put(field.getName(), method.invoke(o));
                    } catch (InvocationTargetException | IllegalAccessException e) {
                        // InvocationTargetException 被调用的方法内部可能有异常抛出没有被捕获，就报这个
                        // IllegalAccessException 没有对这个类的访问权限的时候报这个异常
                        e.printStackTrace();
                    }
                }
            }
        }
        return map;
    }
}
